#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'常用模块'

__author__ = '许文杰'

from datetime import datetime, timedelta, timezone

print(datetime.now())

dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
print(dt)
timestamp = dt.timestamp() # 时间转时间戳
print(timestamp)
date = datetime.fromtimestamp(timestamp) # 时间戳转时间  # 本地时间
date1 = datetime.utcfromtimestamp(timestamp) # UTC时间
print(date)
print(date1)

# str转换为datetime
timeStr = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
print(timeStr)
# datetime转换为str
str = timeStr.strftime('%a, %b, %H:%M')
print(str)

# datetime加减,使用timedelta
now = datetime.now()
print(now + timedelta(hours=24))
print(now + timedelta(days=2, hours=24))

# 本地时间转换为UTC时间
tz_utc_8 = timezone(timedelta(hours=8))
print(datetime.now())
# 时区转换 拿到UTC时间，并强制设置时区为UTC+0:00:
utc_dt = datetime.utcnow().replace(tzinfo=timezone.utc)
print(utc_dt)
# astimezone()将转换时区为北京时间:
bj_dt = utc_dt.astimezone(timezone(timedelta(hours=8)))
print(bj_dt)
# astimezone()将转换时区为东京时间:
tokyo_dt = utc_dt.astimezone(timezone(timedelta(hours=9)))
print(tokyo_dt)
# astimezone()将bj_dt转换时区为东京时间:
tokyo_dt2 = bj_dt.astimezone(timezone(timedelta(hours=9)))
print(tokyo_dt2)


'namedtuple是一个函数，它用来创建一个自定义的tuple对象，并且规定了tuple元素的个数，并可以用属性而不是索引来引用tuple的某个元素'

from collections import namedtuple

# namedtuple('名称', [属性list]):
# 表示一个点
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
print(p.x, p.y)

# 表示一个圆 圆点和半径
Circle = namedtuple('Circle', ['x', 'y', 'r'])
c = Circle(1, 2, 3)
print(c)

# 使用list存储数据时，按索引访问元素很快，但是插入和删除元素就很慢了，因为list是线性存储，数据量大的时候，插入和删除效率很低。
# deque是为了高效实现插入和删除操作的双向列表，适合用于队列和栈：

from  collections import deque

q = deque(['a', 'b'])
q.append('c') # 往后面增加
q.appendleft('z') # 往开头增加
print(q)
q.pop() # 删除最后一个
print(q)
q.popleft() # 删除第一个
print(q)

# 使用dict时，如果引用的Key不存在，就会抛出KeyError。如果希望key不存在时，返回一个默认值，就可以用defaultdict

from collections import defaultdict

dd = defaultdict(lambda :'N/A')
dd['key'] = '123'
print(dd['key'])
print(dd['key1'])

# 使用dict时，Key是无序的。在对dict做迭代时，我们无法确定Key的顺序。
# 如果要保持Key的顺序，可以用OrderedDict

from collections import OrderedDict

od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(od)
# OrderedDict的Key会按照插入的顺序排列
od2 = OrderedDict()
od2['x'] = 1
od2['y'] = 2
od2['z'] = 3
print(od2)

# OrderedDict可以实现一个FIFO（先进先出）的dict，当容量超出限制时，先删除最早添加的Key

class LastUpdatedOrderedDict(OrderedDict):

    def __init__(self, capacity):
        super(LastUpdatedOrderedDict, self).__init__()
        self._capacity = capacity

    def __setitem__(self, key, value):
        containsKey = 1 if key in self else 0
        if len(self) - containsKey >= self._capacity:
            last = self.popitem(last=False)
            print('remove:', last)
        if containsKey:
            del self[key]
            print('set:', (key, value))
        else:
            print('add:', (key, value))
        OrderedDict.__setitem__(self, key, value)

# Counter是一个简单的计数器，例如，统计字符出现的个数：
from collections import Counter

c = Counter()
for ch in 'programming':
    c[ch] = c[ch] + 1
print(c)

# base64的编解码
import base64
print(base64.b64encode(b'binary\x00string'))
print(base64.b64decode(b'YmluYXJ5AHN0cmluZw=='))

# 由于标准的Base64编码后可能出现字符+和/，在URL中就不能直接作为参数，所以又有一种"url safe"的base64编码，其实就是把字符+和/分别变成-和_
print(base64.b64encode(b'i\xb7\x1d\xfb\xef\xff'))
print(base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')) # url safe"的base64编码,把字符+和/分别变成-和_
print(base64.urlsafe_b64decode(b'abcd--__'))

# struct的pack函数把任意数据类型变成bytes
# pack的第一个参数是处理指令，'>I'的意思是：
# >表示字节顺序是big-endian，也就是网络序，I表示4字节无符号整数。
# 后面的参数个数要和处理指令一致。

import struct

print(struct.pack('>I', 10240099))

# unpack把bytes变成相应的数据类型：
print(struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80'))

# MD5
import hashlib

# md5 = hashlib.md5()
# md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
# print(md5.hexdigest())
# 如果数据量很大，可以分块多次调用update()，最后计算的结果是一样的：
md5 = hashlib.md5()
md5.update('how to use md5 in '.encode('utf-8'))
md5.update('python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
# 摘要算法是SHA1，调用SHA1和调用MD5完全类似：
sha = hashlib.sha1()
sha.update('how to use md5 in '.encode('utf-8'))
sha.update('python hashlib?'.encode('utf-8'))
print(sha.hexdigest())

import random

# db = {
#     'michael': 'e10adc3949ba59abbe56e057f20f883e',
#     'bob': '878ef96e86145580c38c87f0410ad153',
#     'alice': '99b1c2188db85afee403b1536010c2c9'}

# def login(user, password):
#
#     md5 = hashlib.md5()
#     md5.update(password.encode('utf-8'))
#
#     passwd = db[user]
#
#     if md5.hexdigest() == passwd:
#         print('login success')
#     else:
#         print('password error')
#
# login('michael', '123456')
# login('bob', 'abc999')
# login('alice', 'alice2008')

# def get_md5(s):
#     return hashlib.md5(s.encode('utf-8')).hexdigest()
#
# class User(object):
#     def __init__(self, username, password):
#         self.username = username
#         self.salt = ''.join([chr(random.randint(48, 122)) for i in range(20)])
#         self.password = get_md5(password + self.salt)
#
# db = {
#     'michael': User('michael', '123456'),
#     'bob': User('bob', 'abc999'),
#     'alice': User('alice', 'alice2008')
# }
#
# def login(username, password):
#     user = db[username]
#     return user.password == get_md5(password+user.salt)
#
# if login('michael', '123456'):
#     print('login success')
# else:
#     print('login fail')

# hmac算法
import hmac

# def hmac_md5(key, s):
#     return hmac.new(key.encode('utf-8'), s.encode('utf-8'), 'MD5').hexdigest()
#
# class User(object):
#     def __init__(self, username, password):
#         self.username = username
#         self.key = ''.join([chr(random.randint(48, 122)) for i in range(20)])
#         self.password = hmac_md5(self.key, password)

# db = {
#     'michael': User('michael', '123456'),
#     'bob': User('bob', 'abc999'),
#     'alice': User('alice', 'alice2008')
# }

# def login(username, password):
#     user = db[username]
#     return user.password == hmac_md5(user.key, password)

# if login('michael', '123456'):
#     print('login success')
# else:
#     print('login fail')

import itertools

# 无限迭代器
# natural = itertools.count(1)
# for n in natural:
#     print(n)
# 通过takewhile()等函数根据条件判断来截取出一个有限的序列：
# ns = itertools.takewhile(lambda x : x <= 10, natural)
# cycles = itertools.cycle('ABC')
# for n in ns:
#     print(n)

# cycle()会把传入的一个序列无限重复下去
# for n in cycles:
#     print(n)
# repeat()负责把一个元素无限重复下去，不过如果提供第二个参数就可以限定重复次数：
# re = itertools.repeat('a', 5)
# for n in re:
#     print(n)
# chain()可以把一组迭代对象串联起来，形成一个更大的迭代器：
# for c in itertools.chain('abc', 'xyz'):
#     print(c)
# groupby()把迭代器中相邻的重复元素挑出来放在一起：
# for key, group in itertools.groupby('aaabbbcccdddd'):
#     print(key, list(group))
# 忽略大小写分组
# for key, group in itertools.groupby('AaaBBbcCAAa', lambda c:c.upper()):
#     print(key, list(group))

from contextlib import contextmanager, closing

# 某段代码执行前后自动执行特定代码
# @contextmanager
# def tag(name):
#     print(name)
#     yield
#     print(name)
# with tag('你要'):
#     print('上天了')
#     print('然后又掉下俩，脸着地')

# from urllib.request import urlopen
#
# with closing(urlopen('https://www.baidu.com')) as page:
#     for line in page:
#         print(line)
#
# @contextmanager
# def closing(thing):
#     try:
#         yield thing
#     finally:
#         thing.colse()

# request模块可以使用get非常方便地抓取URL内容
from urllib import request, parse
# with request.urlopen('https://api.douban.com/v2/book/2129650') as f:
#     data = f.read()
#     print('status', f.status, f.reason)
#     for k, v in f.getheaders():
#         print('%s: %s' % (k, v))
#     print('data==>', data.decode('utf-8'))

# req = request.Request('http://www.douban.com/')
# req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
# with request.urlopen(req) as f:
#     print('status : ', f.status, f.reason)
#     for k, v in f.getheaders():
#         print('%s : %s' %(k, v))
#
#     print('data : ', f.read().decode('utf-8'))

# email = input('email:')
# password = input('password:')
# login_data = parse.urlencode([('username', email),
#     ('password', password),
#     ('entry', 'mweibo'),
#     ('client_id', ''),
#     ('savestate', '1'),
#     ('ec', ''),
#     ('pagerefer', 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F')])
#
# req = request.Request('https://passport.weibo.cn/sso/login')
# req.add_header('Origin', 'https://passport.weibo.cn')
# req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
# req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')
#
# with request.urlopen(req, data=login_data.encode('utf-8')) as f:
#     print('status : ', f.status, f.reason)
#     for k, v in f.getheaders():
#         print('%s : %s' % (k, v))
#     print('data: ', f.read().decode('utf-8'))

# HTML解析
from html.parser import HTMLParser
from html.entities import name2codepoint

# class MyHtmlParser(HTMLParser):
#
#     def handle_starttag(self, tag, attrs):
#         print('<%s>' % tag)
#     def handle_endtag(self, tag):
#         print('<%s>' % tag)
#     def handle_startendtag(self, tag, attrs):
#         print('<%s>' % tag)
#     def handle_data(self, data):
#         print(data)
#     def handle_comment(self, data):
#         print('<!--', data, '-->')
#     def handle_entityref(self, name):
#         print('&%s;' % name)
#     def handle_charref(self, name):
#         print('#&%s;' % name)
#
# parser = MyHtmlParser()
# parser.feed('''<html>
# <head></head>
# <body>
# <!-- test html parser -->
#     <p>Some <a href=\"#\">html</a> HTML&nbsp;tutorial...<br>END</p>
# </body></html>''')


